package com.gitee.wsl.platform.thread.task

import com.gitee.wsl.platform.thread.task.TaskExecutors.sharedBlockingIO
import com.gitee.wsl.platform.thread.task.TaskExecutors.trampoline
import com.gitee.wsl.platform.thread.task.TimedAwait.latchAndExpectCompletion
import com.gitee.wsl.platform.thread.task.VirtualThreads.areVirtualThreadsSupported
import java.util.Objects
import java.util.concurrent.CountDownLatch
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicReference
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotSame
import kotlin.test.assertSame
import kotlin.test.assertTrue


class TaskExecutorTest {
    @Test
    fun trampolineIsTaskExecutor() {
        val ec1 = trampoline()
        val ec2 = TaskExecutor.from(ec1)
        assertEquals(ec1, ec2)
    }

    @Test
    fun trampolineResumeIsExecute() {
        val count = AtomicInteger(0)
        val ec = TaskExecutor.from(trampoline())

        ec.execute {
            count.incrementAndGet()
            ec.resumeOnExecutor { count.incrementAndGet() }
            count.incrementAndGet()
            ec.resumeOnExecutor {
                count.incrementAndGet()
                ec.resumeOnExecutor {
                    count.incrementAndGet()
                    ec.resumeOnExecutor { count.incrementAndGet() }
                }
                count.incrementAndGet()
            }
        }

        assertEquals(7, count.get())
    }

    @Test
    fun sharedBlockingIOIsTaskExecutorOnOlderJava() {
        val sysProp = SysProp.withVirtualThreads(false)
        assertFalse(areVirtualThreadsSupported())
        try {
            val ec1 = sharedBlockingIO()
            val ec2 = TaskExecutor.from(ec1)
            assertEquals(ec1, ec2)
        } finally {
            sysProp.close()
        }
    }

    @Test
    fun sharedBlockingIOIsTaskExecutorOnJava21() {
        assertTrue(
            areVirtualThreadsSupported(),
            "Requires Java 21+"
        )
        val ec1 = sharedBlockingIO()
        val ec2 = TaskExecutor.from(ec1)
        assertEquals(ec1, ec2)
    }

    @Throws(InterruptedException::class)
    private fun testThreadName() {
        val executor = TaskExecutor.from(sharedBlockingIO())
        val threadName = AtomicReference<String?>(null)
        val isComplete = CountDownLatch(1)
        executor.execute {
            threadName.set(Thread.currentThread().name)
            isComplete.countDown()
        }

        latchAndExpectCompletion(isComplete, "isComplete")
        assertTrue(
            Objects.requireNonNull<String?>(threadName.get()).startsWith("tasks-io-"),
            "\"" + threadName.get() + "\".startsWith(\"tasks-io-\")"
        )
    }

    @Test
    @Throws(InterruptedException::class)
    fun testThreadNameOlderJava() {
        val sysProp = SysProp.withVirtualThreads(false)
        assertFalse(areVirtualThreadsSupported())
        try {
            testThreadName()
        } finally {
            sysProp.close()
        }
    }

    @Test
    @Throws(InterruptedException::class)
    fun testThreadNameJava21() {
        assertTrue(
            areVirtualThreadsSupported(),
            "Requires Java 21+"
        )
        testThreadName()
    }

    @Throws(InterruptedException::class)
    private fun testThreadNameNested() {
        val executor = TaskExecutor.from(sharedBlockingIO())
        val threadName1 = AtomicReference<String?>(null)
        val threadName2 = AtomicReference<String?>(null)
        val isComplete = CountDownLatch(1)

        executor.execute {
            threadName1.set(Thread.currentThread().name)
            executor.execute {
                threadName2.set(Thread.currentThread().name)
                isComplete.countDown()
            }
        }

        latchAndExpectCompletion(isComplete, "isComplete")
        assertTrue(
            Objects.requireNonNull<String?>(threadName1.get()).startsWith("tasks-io-"),
            "\"" + threadName1.get() + "\".startsWith(\"tasks-io-\")"
        )
        assertTrue(
            Objects.requireNonNull<String?>(threadName1.get()).startsWith("tasks-io-"),
            "\"" + threadName1.get() + "\".startsWith(\"tasks-io-\")"
        )
        assertNotSame(
            threadName1.get(),
            threadName2.get(),
            "threadName1.get() != threadName2.get()"
        )
    }

    @Test
    @Throws(InterruptedException::class)
    fun testThreadNameNestedOlderJava() {
        val sysProp = SysProp.withVirtualThreads(false)
        assertFalse(areVirtualThreadsSupported())
        sysProp.use { _ ->
            testThreadNameNested()
        }
    }

    @Test
    @Throws(InterruptedException::class)
    fun testThreadNameNestedJava21() {
        assertTrue(
            areVirtualThreadsSupported(),
            "Requires Java 21+"
        )
        testThreadNameNested()
    }

    @Throws(InterruptedException::class)
    private fun testResumeCanSwitchThread() {
        val executor = TaskExecutor.from(sharedBlockingIO())
        val threadName1 = AtomicReference<String?>(null)
        val threadName2 = AtomicReference<String?>(null)
        val isComplete = CountDownLatch(1)

        executor.resumeOnExecutor {
            threadName1.set(Thread.currentThread().name)
            executor.resumeOnExecutor {
                threadName2.set(Thread.currentThread().name)
                isComplete.countDown()
            }
        }

        latchAndExpectCompletion(isComplete, "isComplete")
        for (name in arrayOf<String?>(
            Objects.requireNonNull<String?>(threadName1.get()),
            Objects.requireNonNull<String?>(threadName2.get())
        )) {
            assertTrue(
                Objects.requireNonNull<String>(name).startsWith("tasks-io-"),
                "\"$name\".startsWith(\"tasks-io-\")"
            )
        }
        assertSame(threadName1.get(), threadName2.get())
    }

    @Test
    @Throws(InterruptedException::class)
    fun testResumeCanSwitchThreadOlderJava() {
        val sysProp = SysProp.withVirtualThreads(false)
        assertFalse(areVirtualThreadsSupported())
        try {
            testResumeCanSwitchThread()
        } finally {
            sysProp.close()
        }
    }

    @Test
    @Throws(InterruptedException::class)
    fun testResumeCanSwitchThreadJava21() {
        assertTrue(
            areVirtualThreadsSupported(),
            "Requires Java 21+"
        )
        testResumeCanSwitchThread()
    }

    @Throws(InterruptedException::class)
    private fun resumeCanStayOnTheSameThread() {
        val executor = TaskExecutor.from(sharedBlockingIO())
        val threadName1 = AtomicReference<String?>(null)
        val threadName2 = AtomicReference<String?>(null)
        val isComplete = CountDownLatch(1)

        executor.execute {
            threadName1.set(Thread.currentThread().name)
            executor.resumeOnExecutor {
                threadName2.set(Thread.currentThread().name)
                isComplete.countDown()
            }
        }

        latchAndExpectCompletion(isComplete, "isComplete")
        for (name in arrayOf<String?>(
            Objects.requireNonNull<String?>(threadName1.get()),
            Objects.requireNonNull<String?>(threadName2.get())
        )) {
            assertTrue(
                Objects.requireNonNull<String>(name).startsWith("tasks-io-"),
                "\"$name\".startsWith(\"tasks-io-\")"
            )
        }
        assertSame(threadName1.get(), threadName2.get())
    }

    @Test
    @Throws(InterruptedException::class)
    fun resumeCanStayOnTheSameThreadOlderJava() {
        val sysProp= SysProp.withVirtualThreads(false)
        assertFalse(areVirtualThreadsSupported())
        try {
            resumeCanStayOnTheSameThread()
        } finally {
            sysProp.close()
        }
    }

    @Test
    @Throws(InterruptedException::class)
    fun resumeCanStayOnTheSameThreadJava21() {
        assertTrue(
            areVirtualThreadsSupported(),
            "Requires Java 21+"
        )
        resumeCanStayOnTheSameThread()
    }
}
